// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.

#ifndef CANGJIE_MIGRATOR_UTIL_FILESYSTEM
#define CANGJIE_MIGRATOR_UTIL_FILESYSTEM

#include "Path.h"

namespace CangjieMigrator::Util {

inline Path LexicalNormalize(const Path& input)
{
    // Preserve root name and root directory (drive/UNC + leading slash)
    Path result;
    auto it = input.begin();
    auto end = input.end();

    if (input.has_root_name()) {
        if (it != end) {
            result /= *it; // root_name
            ++it;
        }
    }
    if (input.has_root_directory()) {
        result /= Path("/"); // root_directory
        // Note: some implementations yield root_directory as an iterator element; we keep it explicit.
        if (it != end && *it == Path("/")) {
            ++it; // skip if already present as a component
        }
    }

    std::vector<Path> stack;
    for (; it != end; ++it) {
        const Path& comp = *it;
        if (comp == ".") {
            continue;
        }
        if (comp == "..") {
            if (!stack.empty() && stack.back() != "..") {
                stack.pop_back();
            } else {
                if (!input.has_root_directory()) {
                    stack.emplace_back("..");
                }
            }
            continue;
        }
        stack.push_back(comp);
    }

    for (const auto& seg : stack) {
        result /= seg;
    }

    if (result.empty()) {
        return Path(".");
    }
    return result;
}

inline Path LexicalRelative(const Path& target, const Path& base)
{
    // Unify coordinate system: if one is absolute and the other is relative, make both absolute
    Path absTarget = target;
    Path absBase = base;
    if (absTarget.is_absolute() != absBase.is_absolute()) {
        Path cwd = FileSystem::current_path();
        if (absTarget.is_relative()) {
            absTarget = cwd / absTarget;
        }
        if (absBase.is_relative()) {
            absBase = cwd / absBase;
        }
    }

    // Normalize both paths lexically (no filesystem access)
    absTarget = LexicalNormalize(absTarget);
    absBase = LexicalNormalize(absBase);

    // Different roots (e.g., different drives/UNC roots or root-dir mismatch) -> cannot compute relative
    if (absTarget.has_root_name() || absBase.has_root_name()) {
        if (absTarget.root_name() != absBase.root_name()) {
            return absTarget;
        }
    }
    if (absTarget.has_root_directory() != absBase.has_root_directory()) {
        return absTarget;
    }

    // Find longest common prefix, skipping root parts
    auto targetIt = absTarget.begin();
    auto targetEnd = absTarget.end();
    auto baseIt = absBase.begin();
    auto baseEnd = absBase.end();

    if (absTarget.has_root_name() && targetIt != targetEnd && baseIt != baseEnd && *targetIt == *baseIt) {
        ++targetIt;
        ++baseIt; // skip root_name
    }
    if (absTarget.has_root_directory() && targetIt != targetEnd && baseIt != baseEnd && *targetIt == *baseIt) {
        ++targetIt;
        ++baseIt; // skip root_directory
    }

    for (; targetIt != targetEnd && baseIt != baseEnd && *targetIt == *baseIt; ++targetIt, ++baseIt) {
    }

    // ".." for the remaining base components
    Path result;
    for (auto it = baseIt; it != baseEnd; ++it) {
        if (*it == ".") {
            continue;
        }
        result /= "..";
    }

    // Append the remaining target components
    for (auto it = targetIt; it != targetEnd; ++it) {
        if (*it == ".") {
            continue;
        }
        result /= *it;
    }

    if (result.empty()) {
        return Path(".");
    }

    // Normalize the result lexically to collapse any incidental "." or ".."
    return LexicalNormalize(result);
}

inline bool IsSubDir(const Path &base, const Path &dir)
{
    auto rel = LexicalRelative(dir, base);
    for (const auto &part : rel) {
        if (part == "..") {
            return false;
        }
    }
    return true;
}

} // namespace CangjieMigrator::Util

#endif // CANGJIE_MIGRATOR_UTIL_FILESYSTEM
